/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2012 All Rights Reserved.
 */
package com.adinnet.common.outbound.pay.utils;

import com.alipay.api.internal.util.StreamUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import java.io.*;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * 
 * @author runzhi
 */
public class AlipaySignature {


    private static final Logger LOGGER = LogManager.getLogger(AlipaySignature.class);


    public static boolean rsaCheckV1(Map<String, String> params, String publicKey,
                                     String charset,String signType) throws AlipayApiException {
        String sign = params.get("sign");
        String content = getSignCheckContentV1(params);

        LOGGER.error("参数》》》》》" + content);
        return rsaCheck(content, sign, publicKey, charset,signType);
    }
    public static boolean rsaCheck(String content, String sign, String publicKey, String charset,
                                   String signType) throws AlipayApiException {

        if (SignConstants.SIGN_TYPE_RSA.equals(signType)) {
            LOGGER.error("SIGN_TYPE_RSA》》》》》》》》》》》》》》》》》》》》》》》》》》》》》" );
            return rsaCheckContent(content, sign, publicKey, charset);

        } else if (SignConstants.SIGN_TYPE_RSA2.equals(signType)) {
            LOGGER.error("SIGN_TYPE_RSA2》》》》》》》》》》》》》》》》》》》》》》》》》》》》》" );
            return rsa256CheckContent(content, sign, publicKey, charset);

        } else {
            LOGGER.error("失败》》》》》》》》》》》》》》》》》》》》》》》》》》》》》" );
            throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
        }

    }
    public static boolean rsa256CheckContent(String content, String sign, String publicKey,
                                             String charset) throws AlipayApiException {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA",
                    new ByteArrayInputStream(publicKey.getBytes()));

            java.security.Signature signature = java.security.Signature
                    .getInstance(SignConstants.SIGN_SHA256RSA_ALGORITHMS);

            signature.initVerify(pubKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            LOGGER.error(">>>>>>>AAAAAA");
            throw new AlipayApiException(
                    "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
        }
    }



    public static String getSignCheckContentV1(Map<String, String> params) {
        if (params == null) {
            return null;
        }

        params.remove("sign");
        params.remove("sign_type");

        StringBuffer content = new StringBuffer();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            content.append((i == 0 ? "" : "&") + key + "=" + value);
        }

        return content.toString();
    }
    /**
     * 验证签名
     * @param params 参数列表(包括待验签参数和签名值sign) key-参数名称 value-参数值
     * @param publicKey 验签公钥
     * @param charset 验签字符集
     *
     **/
    public static boolean rsaCheckV(Map<String, String> params, String publicKey,
                                     String charset) throws AlipayApiException {
        String sign = params.get("sign");
        String content = null;
        try {
            content = getSignCheckContentV(params , Boolean.TRUE).get("contentEnc");
            LOGGER.error(">>>>>>>>>>>safsads" + content);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return rsaCheckContent(content, sign, publicKey, charset);
    }

    /**
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     *
     * @param params 需要排序并参与字符拼接的参数组
     * @param flag 是否编码
     * @return 拼接后字符串
     */
    public static Map<String ,String> getSignCheckContentV(Map<String, String> params , boolean flag) throws UnsupportedEncodingException {
        if (params == null) {
            return null;
        }

        params.remove("sign");

        Map<String ,String> map = new HashMap<>();
        StringBuffer content = new StringBuffer();
        StringBuffer contentEnc = new StringBuffer();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            content.append((i == 0 ? "" : "&") + key + "=" + value);
            if(flag){
                contentEnc.append((i == 0 ? "" : "&") + key + "=" + URLEncoder.encode(value ,"UTF-8"));
            }
        }

        map.put("content" , content.toString());
        map.put("contentEnc" , contentEnc.toString());
        return map;
    }

    /**
     * 验证数据
     * @param content
     * @param sign
     * @param publicKey
     * @param charset
     * @return
     * @throws AlipayApiException
     */
    public static boolean rsaCheckContent(String content, String sign, String publicKey,
                                          String charset) throws AlipayApiException {
        try {
            PublicKey pubKey = getPublicKeyFromX509(SignConstants.SIGN_TYPE_RSA,
                    new ByteArrayInputStream(publicKey.getBytes()));

            java.security.Signature signature = java.security.Signature
                    .getInstance(SignConstants.SIGN_ALGORITHMS);

            signature.initVerify(pubKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new AlipayApiException(
                    "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
        }
    }

    public static PublicKey getPublicKeyFromX509(String algorithm,
                                                 InputStream ins) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        StringWriter writer = new StringWriter();
        StreamUtil.io(new InputStreamReader(ins), writer);

        byte[] encodedKey = writer.toString().getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }

    /**
     * sha1WithRsa 加签
     *
     * @param content 待签名字符串
     * @param privateKey 商户私钥
     * @param charset 字符集
     * @return
     * @throws AlipayApiException
     */
    public static String rsaSign(String content, String privateKey,
                                 String charset) throws AlipayApiException {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(SignConstants.SIGN_TYPE_RSA,
                    new ByteArrayInputStream(privateKey.getBytes()));

            java.security.Signature signature = java.security.Signature
                    .getInstance(SignConstants.SIGN_ALGORITHMS);

            signature.initSign(priKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();

            return new String(Base64.encodeBase64(signed));
        } catch (InvalidKeySpecException ie) {
            throw new AlipayApiException("RSA私钥格式不正确，请检查是否正确配置了PKCS8格式的私钥", ie);
        } catch (Exception e) {
            throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
        }
    }

    /**
     * sha256WithRsa 加签
     *

     * @param content 加密内容
     * @param privateKey 加密私钥
     * @param charset 加密字符集
     * @return
     * @throws AlipayApiException
     */
    public static String rsa256Sign(String content, String privateKey,
                                    String charset) throws AlipayApiException {

        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(SignConstants.SIGN_TYPE_RSA,
                    new ByteArrayInputStream(privateKey.getBytes()));

            java.security.Signature signature = java.security.Signature
                    .getInstance(SignConstants.SIGN_SHA256RSA_ALGORITHMS);

            signature.initSign(priKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();

            return new String(Base64.encodeBase64(signed));
        } catch (Exception e) {
            throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
        }

    }

    public static PrivateKey getPrivateKeyFromPKCS8(String algorithm,
                                                    InputStream ins) throws Exception {
        if (ins == null || StringUtils.isEmpty(algorithm)) {
            return null;
        }

        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        byte[] encodedKey = StreamUtil.readText(ins).getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }


}
